<link rel="import" href="../../bower_components/polymer/polymer.html">

<!--
hydrogen-set is a plumber component that is driven by events
and produces data in the form of an upward-bindable set.
It provides event handler functions like event-add and
event-del, which may be attached elsewhere as event listeners.
The hydrogen-set then captures those events and adds or
removes their details to an internal set, which it
publishes as out-value.

As an example, you may have a list of input widgets that
generate strings by firing an event. If you attach
the hydrogen-set `event-add` function as the handler
for the input widgets' input event, then the hydrogen-set
will collect and deduplicate all of the strings the
user generated.

Thus, hydrogen-set is useful for capturing semantic
events from across an application, and organizing
the data generated into a single store.

Example:

  <hydrogen-set
    event-add="{{add}}
    event-delete="{{del}}
    out-value="{{selected}}"
  ></hydrogen-set>
  <element-one
    selected="{{selected}}"
    on-select="add"
    on-deselect="del"
  ></element-one>
  <element-two
    selected="{{selected}}"
    on-select="add"
    on-deselect="del"
  ></element-two>

@demo demo/index.html
-->
<script>
Polymer({
  is: 'hydrogen-set',
  properties: {
    /**
    * A function to bind to event callback where
    * the detail value is the item to add.
    *
    * @property eventAdd
    * @type Function
    */
    eventAdd: {
      readOnly: true,
      notify: true,
      type: Function,
      value: function() {
        return function(e) {
          this.add(e.detail);
        }.bind(this);
      }
    },
    /**
    * A function to bind to event callback where
    * the detail value is the item to remove.
    *
    * @property eventDelete
    * @type Function
    */
    eventDelete: {
      readOnly: true,
      notify: true,
      type: Function,
      value: function() {
        return function(e) {
          this.delete(e.detail);
        }.bind(this);
      }
    },
    /**
    * A function to bind to event callback where
    * the detail value is the list of items that should
    * replace the current set.
    *
    * @property eventUpdate
    * @type Function
    */
    eventUpdate: {
      readOnly: true,
      notify: true,
      type: Function,
      value: function() {
        return function(e) {
          this.update(e.detail);
        }.bind(this);
      }
    },
    /**
    * A function to bind to event callback
    * which when called reset the set to the
    * empty set ([]).
    *
    * @property eventClear
    * @type Function
    */
    eventClear: {
      readOnly: true,
      notify: true,
      type: Function,
      value: function() {
        return function(e) {
          this.clear(e.detail);
        }.bind(this);
      }
    },
    /**
    * The read-only array representing the set of
    * items in this set.
    *
    * @property outValue
    * @type Array
    * @default []
    */
    outValue: {
      type: Array,
      readOnly: true,
      notify: true,
      value: function() {
        return [];
      }
    }
  },
  /**
   * Adds an item to the set.
   */
  add: function(value) {
    if (this.outValue.indexOf(value) >= 0) { return; }
    this.push('outValue', value);
  },
  /**
   * Removes an item from the set.
   */
  delete: function(value) {
    var i = this.outValue.indexOf(value);
    if (i < 0) { return; }
    this.splice('outValue', i, 1);
  },
  /**
   * Sets the set to a specific array of items.
   */
  update: function(value) {
    if (value.constructor === Array) {
      var uniq = {};
      var list = [];
      for (var i=0, l = value.length; i < l; ++i) {
        var item = value[i];
        if (uniq.hasOwnProperty(item)) {
          continue;
        }
        list.push(item);
        uniq[item] = true;
      }
      this._setOutValue(list)
    }
  },
  /**
   * Resets the set to the empty set.
   */
  clear: function() {
    this.update([]);
  }
});
</script>
